-- avifenc -q [0,100] --qalpha [0,100] -s 0 -d 8 -y [400|420|444] (--sharpyuv) --ignore-exif --ignore-xmp --ignore-icc [input.png|jpg] [output.avif]
-- avifdec [input.avif] [output.png]
-- VIF FORMAT: [4]VIF{profile} [varint]width [varint]height ([varint]colorDataSize) colorData (alphaData)
-- VIF now supports 8-bit depth, full range, YUV400|420|444, noAlpha or hasAlpha, single frame, no sequence, no layer, no exif, no xmp, no icc
-- VIF now supports these profiles:
-- 1: YUV400 noAlpha  (Y)
-- 2: YUV400 hasAlpha (Y+A)
-- 3: YUV420 noAlpha  (Yuv)
-- 4: YUV420 hasAlpha (Yuv+A)
-- 5: YUV444 noAlpha  (YUV)
-- 6: YUV444 hasAlpha (YUV+A)

local string = string
local format = string.format
local char = string.char
local floor = math.floor
local tonumber = tonumber
local assert = assert
local print = print
local io = io
local write = io.write
local open = io.open
local arg = arg

local whPos = {
	[1] = 0xb9,
	[2] = 0xfb,
	[3] = 0xbd,
	[4] = 0xff,
	[5] = 0xbd,
	[6] = 0xff,
}

local pnPos = { -- +0xe for pnAlpha
	[1] = 0x6d,
	[2] = 0x6d,
	[3] = 0x71,
	[4] = 0x71,
	[5] = 0x71,
	[6] = 0x71,
}

local hasAlpha = {
	[2] = true,
	[4] = true,
	[6] = true,
}

local templates = {
	[1] =
[[
[  0] 00 00 00 1C 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 00 00 00 E9
[ 20] 6D 65 74 61 00 00 00 00 00 00 00 21 68 64 6C 72
[ 30] 00 00 00 00 00 00 00 00 70 69 63 74 00 00 00 00
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 0E 70 69 74
[ 50] 6D 00 00 00 00 00 01 00 00 00 1E 69 6C 6F 63 00
[ 60] 00 00 00 44 00 00 01 00 01 00 00 00 01 00 00 01
[ 70] 0D 00 00 06 AB 00 00 00 28 69 69 6E 66 00 00 00
[ 80] 00 00 01 00 00 00 1A 69 6E 66 65 02 00 00 00 00
[ 90] 01 00 00 61 76 30 31 43 6F 6C 6F 72 00 00 00 00
[ A0] 68 69 70 72 70 00 00 00 49 69 70 63 6F 00 00 00
[ B0] 14 69 73 70 65 00 00 00 00 00 00 00 FD 00 00 00
[ C0] FE 00 00 00 0E 70 69 78 69 00 00 00 00 01 08 00
[ D0] 00 00 0C 61 76 31 43 81 00 1C 00 00 00 00 13 63
[ E0] 6F 6C 72 6E 63 6C 78 00 01 00 0D 00 06 80 00 00
[ F0] 00 17 69 70 6D 61 00 00 00 00 00 00 00 01 00 01
[100] 04 01 02 83 04
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	[2] =
[[
[  0] 00 00 00 1C 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 00 00 01 6A
[ 20] 6D 65 74 61 00 00 00 00 00 00 00 21 68 64 6C 72
[ 30] 00 00 00 00 00 00 00 00 70 69 63 74 00 00 00 00
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 0E 70 69 74
[ 50] 6D 00 00 00 00 00 01 00 00 00 2C 69 6C 6F 63 00
[ 60] 00 00 00 44 00 00 02 00 01 00 00 00 01 00 00 05
[ 70] DD 00 00 07 37 00 02 00 00 00 01 00 00 01 8E 00
[ 80] 00 04 4F 00 00 00 42 69 69 6E 66 00 00 00 00 00
[ 90] 02 00 00 00 1A 69 6E 66 65 02 00 00 00 00 01 00
[ A0] 00 61 76 30 31 43 6F 6C 6F 72 00 00 00 00 1A 69
[ B0] 6E 66 65 02 00 00 00 00 02 00 00 61 76 30 31 41
[ C0] 6C 70 68 61 00 00 00 00 1A 69 72 65 66 00 00 00
[ D0] 00 00 00 00 0E 61 75 78 6C 00 02 00 01 00 01 00
[ E0] 00 00 A7 69 70 72 70 00 00 00 81 69 70 63 6F 00
[ F0] 00 00 14 69 73 70 65 00 00 00 00 00 00 00 FD 00
[100] 00 00 FE 00 00 00 0E 70 69 78 69 00 00 00 00 01
[110] 08 00 00 00 0C 61 76 31 43 81 00 1C 00 00 00 00
[120] 13 63 6F 6C 72 6E 63 6C 78 00 01 00 0D 00 06 80
[130] 00 00 00 38 61 75 78 43 00 00 00 00 75 72 6E 3A
[140] 6D 70 65 67 3A 6D 70 65 67 42 3A 63 69 63 70 3A
[150] 73 79 73 74 65 6D 73 3A 61 75 78 69 6C 69 61 72
[160] 79 3A 61 6C 70 68 61 00 00 00 00 1E 69 70 6D 61
[170] 00 00 00 00 00 00 00 02 00 01 04 01 02 83 04 00
[180] 02 04 01 02 83 05
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	[3] =
[[
[  0] 00 00 00 20 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 4D 41 31 42
[ 20] 00 00 00 EB 6D 65 74 61 00 00 00 00 00 00 00 21
[ 30] 68 64 6C 72 00 00 00 00 00 00 00 00 70 69 63 74
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 50] 0E 70 69 74 6D 00 00 00 00 00 01 00 00 00 1E 69
[ 60] 6C 6F 63 00 00 00 00 44 00 00 01 00 01 00 00 00
[ 70] 01 00 00 01 13 00 00 07 75 00 00 00 28 69 69 6E
[ 80] 66 00 00 00 00 00 01 00 00 00 1A 69 6E 66 65 02
[ 90] 00 00 00 00 01 00 00 61 76 30 31 43 6F 6C 6F 72
[ A0] 00 00 00 00 6A 69 70 72 70 00 00 00 4B 69 70 63
[ B0] 6F 00 00 00 14 69 73 70 65 00 00 00 00 00 00 00
[ C0] FD 00 00 00 FE 00 00 00 10 70 69 78 69 00 00 00
[ D0] 00 03 08 08 08 00 00 00 0C 61 76 31 43 81 00 0C
[ E0] 00 00 00 00 13 63 6F 6C 72 6E 63 6C 78 00 01 00
[ F0] 0D 00 06 80 00 00 00 17 69 70 6D 61 00 00 00 00
[100] 00 00 00 01 00 01 04 01 02 83 04
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	[4] =
[[
[  0] 00 00 00 20 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 4D 41 31 42
[ 20] 00 00 01 86 6D 65 74 61 00 00 00 00 00 00 00 21
[ 30] 68 64 6C 72 00 00 00 00 00 00 00 00 70 69 63 74
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 50] 0E 70 69 74 6D 00 00 00 00 00 01 00 00 00 2C 69
[ 60] 6C 6F 63 00 00 00 00 44 00 00 02 00 01 00 00 00
[ 70] 01 00 00 05 FD 00 00 07 B0 00 02 00 00 00 01 00
[ 80] 00 01 AE 00 00 04 4F 00 00 00 42 69 69 6E 66 00
[ 90] 00 00 00 00 02 00 00 00 1A 69 6E 66 65 02 00 00
[ A0] 00 00 01 00 00 61 76 30 31 43 6F 6C 6F 72 00 00
[ B0] 00 00 1A 69 6E 66 65 02 00 00 00 00 02 00 00 61
[ C0] 76 30 31 41 6C 70 68 61 00 00 00 00 1A 69 72 65
[ D0] 66 00 00 00 00 00 00 00 0E 61 75 78 6C 00 02 00
[ E0] 01 00 01 00 00 00 C3 69 70 72 70 00 00 00 9D 69
[ F0] 70 63 6F 00 00 00 14 69 73 70 65 00 00 00 00 00
[100] 00 00 FD 00 00 00 FE 00 00 00 10 70 69 78 69 00
[110] 00 00 00 03 08 08 08 00 00 00 0C 61 76 31 43 81
[120] 00 0C 00 00 00 00 13 63 6F 6C 72 6E 63 6C 78 00
[130] 01 00 0D 00 06 80 00 00 00 0E 70 69 78 69 00 00
[140] 00 00 01 08 00 00 00 0C 61 76 31 43 81 00 1C 00
[150] 00 00 00 38 61 75 78 43 00 00 00 00 75 72 6E 3A
[160] 6D 70 65 67 3A 6D 70 65 67 42 3A 63 69 63 70 3A
[170] 73 79 73 74 65 6D 73 3A 61 75 78 69 6C 69 61 72
[180] 79 3A 61 6C 70 68 61 00 00 00 00 1E 69 70 6D 61
[190] 00 00 00 00 00 00 00 02 00 01 04 01 02 83 04 00
[1A0] 02 04 01 05 86 07
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	[5] =
[[
[  0] 00 00 00 20 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 4D 41 31 41
[ 20] 00 00 00 EB 6D 65 74 61 00 00 00 00 00 00 00 21
[ 30] 68 64 6C 72 00 00 00 00 00 00 00 00 70 69 63 74
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 50] 0E 70 69 74 6D 00 00 00 00 00 01 00 00 00 1E 69
[ 60] 6C 6F 63 00 00 00 00 44 00 00 01 00 01 00 00 00
[ 70] 01 00 00 01 13 00 00 07 BF 00 00 00 28 69 69 6E
[ 80] 66 00 00 00 00 00 01 00 00 00 1A 69 6E 66 65 02
[ 90] 00 00 00 00 01 00 00 61 76 30 31 43 6F 6C 6F 72
[ A0] 00 00 00 00 6A 69 70 72 70 00 00 00 4B 69 70 63
[ B0] 6F 00 00 00 14 69 73 70 65 00 00 00 00 00 00 00
[ C0] FD 00 00 00 FE 00 00 00 10 70 69 78 69 00 00 00
[ D0] 00 03 08 08 08 00 00 00 0C 61 76 31 43 81 20 00
[ E0] 00 00 00 00 13 63 6F 6C 72 6E 63 6C 78 00 01 00
[ F0] 0D 00 06 80 00 00 00 17 69 70 6D 61 00 00 00 00
[100] 00 00 00 01 00 01 04 01 02 83 04
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	[6] =
[[
[  0] 00 00 00 20 66 74 79 70 61 76 69 66 00 00 00 00
[ 10] 61 76 69 66 6D 69 66 31 6D 69 61 66 4D 41 31 41
[ 20] 00 00 01 86 6D 65 74 61 00 00 00 00 00 00 00 21
[ 30] 68 64 6C 72 00 00 00 00 00 00 00 00 70 69 63 74
[ 40] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 50] 0E 70 69 74 6D 00 00 00 00 00 01 00 00 00 2C 69
[ 60] 6C 6F 63 00 00 00 00 44 00 00 02 00 01 00 00 00
[ 70] 01 00 00 05 FD 00 00 08 1E 00 02 00 00 00 01 00
[ 80] 00 01 AE 00 00 04 4F 00 00 00 42 69 69 6E 66 00
[ 90] 00 00 00 00 02 00 00 00 1A 69 6E 66 65 02 00 00
[ A0] 00 00 01 00 00 61 76 30 31 43 6F 6C 6F 72 00 00
[ B0] 00 00 1A 69 6E 66 65 02 00 00 00 00 02 00 00 61
[ C0] 76 30 31 41 6C 70 68 61 00 00 00 00 1A 69 72 65
[ D0] 66 00 00 00 00 00 00 00 0E 61 75 78 6C 00 02 00
[ E0] 01 00 01 00 00 00 C3 69 70 72 70 00 00 00 9D 69
[ F0] 70 63 6F 00 00 00 14 69 73 70 65 00 00 00 00 00
[100] 00 00 FD 00 00 00 FE 00 00 00 10 70 69 78 69 00
[110] 00 00 00 03 08 08 08 00 00 00 0C 61 76 31 43 81
[120] 20 00 00 00 00 00 13 63 6F 6C 72 6E 63 6C 78 00
[130] 01 00 0D 00 06 80 00 00 00 0E 70 69 78 69 00 00
[140] 00 00 01 08 00 00 00 0C 61 76 31 43 81 00 1C 00
[150] 00 00 00 38 61 75 78 43 00 00 00 00 75 72 6E 3A
[160] 6D 70 65 67 3A 6D 70 65 67 42 3A 63 69 63 70 3A
[170] 73 79 73 74 65 6D 73 3A 61 75 78 69 6C 69 61 72
[180] 79 3A 61 6C 70 68 61 00 00 00 00 1E 69 70 6D 61
[190] 00 00 00 00 00 00 00 02 00 01 04 01 02 83 04 00
[1A0] 02 04 01 05 86 07
]],--  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
}

local f = open(arg[1], "rb")
local s = f:read "*a"
f:close()

local function readIntBE(s, i)
	local a,b,c,d = s:byte(i+1), s:byte(i+2), s:byte(i+3), s:byte(i+4)
	return a * 0x1000000 + b * 0x10000 + c * 0x100 + d
end

local function writeIntBE(s, i, v)
	local a = floor(v / 0x1000000)
	local b = floor(v / 0x10000) % 0x100
	local c = floor(v / 0x100) % 0x100
	local d = v % 0x100
	return s:sub(1,i) .. char(a,b,c,d) .. s:sub(i+5,-1)
end

local function toVarInt(v)
	if v < 0x80 then return char(v) end
	if v < 0x4000 then return char(floor(v / 0x80) + 0x80, v % 0x80) end
	if v < 0x200000 then return char(floor(v / 0x4000) + 0x80, floor(v / 0x80 % 0x80) + 0x80, v % 0x80) end
	if v < 0x10000000 then return char(floor(v / 0x200000) + 0x80, floor(v / 0x4000 % 0x80) + 0x80, floor(v / 0x80 % 0x80) + 0x80, v % 0x80) end
	char(floor(v / 0x10000000) + 0x80, floor(v / 0x200000 % 0x80) + 0x80, floor(v / 0x4000 % 0x80) + 0x80, floor(v / 0x80 % 0x80) + 0x80, v % 0x80)
end

local n = s:find "mdat" + 3
local h = s:sub(1, n)
if not arg[2] then
	for i = 1, (n - 8) do
		if i % 16 == 1 then write(format("[%3X]", i - 1)) end
		write(format(" %02X", h:byte(i)))
		if i % 16 == 0 then print() end
	end
	return
end

local b = h:byte((h:find "iloc" + 11)) -- 1:noAlpha; 2:hasAlpha
assert(b == 1 or b == 2)
local hasAlpha = b == 2
b = h:byte((h:find "av1C" + 6)) -- 0:444; 0x08:422; 0x0c:420; 0x1c:400/alpha
assert(b == 0 or b == 0x0c or b == 0x1c)
local profile
	if b == 0    then profile = hasAlpha and 6 or 5
elseif b == 0x0c then profile = hasAlpha and 4 or 3
elseif b == 0x1c then profile = hasAlpha and 2 or 1 end

local width    = readIntBE(h, whPos[profile])
local height   = readIntBE(h, whPos[profile] + 4)
assert(width  > 0)
assert(height > 0)
local colorPos = readIntBE(h, pnPos[profile])
local colorLen = readIntBE(h, pnPos[profile] + 4)
local alphaPos, alphaLen
if hasAlpha then
	  alphaPos = readIntBE(h, pnPos[profile] + 0xe)
	  alphaLen = readIntBE(h, pnPos[profile] + 0xe + 4)
end

local hh = templates[profile]:gsub("%[.-%]", ""):gsub("%X", ""):gsub("%x%x", function(a) return char(tonumber(a, 16)) end)
	hh = writeIntBE(hh, whPos[profile], width)
	hh = writeIntBE(hh, whPos[profile] + 4, height)
	hh = writeIntBE(hh, pnPos[profile], colorPos)
	hh = writeIntBE(hh, pnPos[profile] + 4, colorLen)
if hasAlpha then
	hh = writeIntBE(hh, pnPos[profile] + 0xe, alphaPos)
	hh = writeIntBE(hh, pnPos[profile] + 0xe + 4, alphaLen)
end
	hh = writeIntBE(hh, #hh, colorLen + (alphaLen or 0) + 8)
	hh = hh .. "mdat"
assert(h == hh)
assert(#s == #h + colorLen + (alphaLen or 0))
assert(colorPos == #h or colorPos == alphaPos + alphaLen)
if hasAlpha then
	assert(alphaPos == #h or alphaPos == colorPos + colorLen)
end

f = io.open(arg[2], "wb")
f:write("VIF", char(profile), toVarInt(width - 1), toVarInt(height - 1))
if hasAlpha then
	f:write(toVarInt(alphaLen))
	f:write(s:sub(alphaPos+1, alphaPos+alphaLen))
end
f:write(s:sub(colorPos+1, colorPos+colorLen))
f:close()

print(format("OK! profile=%d, width=%d, height=%d, alphaLen=%d, colorLen=%d", profile, width, height, alphaLen or 0, colorLen))
